package org.geektimes.configuration.microprofile.config;

import org.apache.commons.lang.StringUtils;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;

public class CustomConfig implements Config {

    private List<ConfigSource> configSources = new ArrayList<>();

    private Map<Class, Converter> converters = new HashMap<>(16);

    private static Comparator<ConfigSource> configSourceComparator = Comparator.comparing(ConfigSource::getOrdinal);


    public CustomConfig() {
        ClassLoader classLoader = CustomConfig.class.getClassLoader();
        ServiceLoader<ConfigSource> serviceLoader = ServiceLoader.load(ConfigSource.class, classLoader);
        serviceLoader.forEach(configSources::add);
        // 排序
        configSources.sort(configSourceComparator);

        ServiceLoader<Converter> converterServiceLoader = ServiceLoader.load(Converter.class, classLoader);
        converterServiceLoader.forEach(converter -> {
                    // 获得泛型接口上的泛型类型
                    ParameterizedType type = (ParameterizedType) converter.getClass().getGenericInterfaces()[0];
                    Class klass = (Class) type.getActualTypeArguments()[0];
                    converters.put(klass, converter);
                }
        );
    }

    @Override
    public <T> T getValue(String propertyName, Class<T> propertyType) {
        String propertyValue = getPropertyValue(propertyName);

        // String 转换成目标类型
        if (propertyType == String.class)
            return (T) propertyValue;       // 应该允许返回null

        return (T) converters.get(propertyType).convert(propertyValue);
    }

    @Override
    public ConfigValue getConfigValue(String propertyName) {
        return null;
    }

    @Override
    public <T> List<T> getValues(String propertyName, Class<T> propertyType) {
        return null;
    }

    @Override
    public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {

        return Optional.ofNullable(getValue(propertyName, propertyType));
    }

    @Override
    public <T> Optional<List<T>> getOptionalValues(String propertyName, Class<T> propertyType) {
        return Optional.empty();
    }

    @Override
    public Iterable<String> getPropertyNames() {
        Set<String> iter = new HashSet<>();
        getConfigSources().forEach(src ->
            iter.addAll(src.getPropertyNames())
        );
        return iter;
    }

    @Override
    public Iterable<ConfigSource> getConfigSources() {
        return Collections.unmodifiableCollection(configSources);
    }

    @Override
    public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
        return Optional.ofNullable(converters.get(forType));
    }

    @Override
    public <T> T unwrap(Class<T> type) {
        try {
            return type.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected String getPropertyValue(String propertyName) {
        for (ConfigSource configSource : configSources) {
            String propertyValue = configSource.getValue(propertyName);
            if (Objects.nonNull(propertyValue)) {
                return propertyValue;
            }
        }
        return null;
    }

}
